import json
import re

from QQLoginTool.QQtool import OAuthQQ
from django.conf import settings
from django.contrib.auth import login
from django.http import JsonResponse, HttpResponseForbidden, HttpResponseServerError
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from django_redis import get_redis_connection
from pymysql import DatabaseError

from apps.carts.utils import merge_cart_cookie_to_redis
from apps.oauth.models import OAuthQQUser, OAuthSinaUser
from apps.users.models import User
from utils import sinaweibopy3
from utils.response_code import RETCODE
from utils.secret import SecretOauth
from . import constants


class QQAuthURLView(View):
    """提供QQ登录页面网址
    https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&state=xxx
    """

    def get(self, request):
        # next表示从哪个页面进入到的登录页面，将来登录成功后，就自动回到那个页面
        next = request.GET.get('next')

        # 获取QQ登录页面网址
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI, state=next)
        login_url = oauth.get_qq_url()

        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'login_url': login_url})


class QQAuthUserView(View):
    """用户扫码登录的回调处理"""

    def get(self, request):
        """Oauth2.0认证"""
        code = request.GET.get('code')
        if not code:
            return HttpResponseForbidden('缺少code')

        # 创建工具对象
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI)

        try:
            # 使用code向QQ服务器请求access_token
            access_token = oauth.get_access_token(code)
            # 使用access_token向QQ服务器请求openid
            openid = oauth.get_open_id(access_token)
        except Exception as e:
            settings.logger.error(e)
            return HttpResponseServerError('OAuth2.0认证失败')

        try:
            oauth_user = OAuthQQUser.objects.get(openid=openid)

        except OAuthQQUser.DoesNotExist:
            # 如果openid没绑定美多商城用户
            # 加密 openid--使用itsdangerous
            secret_openid = SecretOauth().dumps({'openid': openid})
            context = {'openid': secret_openid}
            return render(request, 'oauth_callback.html', context)

        else:
            # 如果openid已绑定美多商城用户
            # 实现状态保持
            qq_user = oauth_user.user
            login(request, qq_user)

            # 重定向到主页
            response = redirect(reverse('contents:index'))
            # 登录时用户名写入到cookie，有效期15天
            response.set_cookie('username', qq_user.username, max_age=3600 * 24 * 15)
            return response

    def post(self, request):
        """美多商城用户绑定到openid"""
        # 接收参数
        mobile = request.POST.get('mobile')
        pwd = request.POST.get('password')
        sms_code_client = request.POST.get('sms_code')
        openid = request.POST.get('openid')

        # 校验参数
        # 判断参数是否齐全
        if not all([mobile, pwd, sms_code_client]):
            return HttpResponseForbidden('缺少必传参数')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseForbidden('请输入正确的手机号码')
        # 判断密码是否合格
        if not re.match(r'^[0-9A-Za-z]{8,20}$', pwd):
            return HttpResponseForbidden('请输入8-20位的密码')
        # 判断短信验证码是否一致
        redis_conn = get_redis_connection('sms_code')
        sms_code_server = redis_conn.get('sms_%s' % mobile)
        if sms_code_server is None:
            return render(request, 'oauth_callback.html', {'sms_code_errmsg': '无效的短信验证码'})
        if sms_code_client != sms_code_server.decode():
            return render(request, 'oauth_callback.html', {'sms_code_errmsg': '输入短信验证码有误'})
        # 解密出openid 再判断openid是否有效
        openid = SecretOauth().loads(openid).get('openid')
        if not openid:
            return render(request, 'oauth_callback.html', {'openid_errmsg': '无效的openid'})

        # 保存注册数据
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            # 用户不存在,新建用户
            user = User.objects.create_user(username=mobile, password=pwd, mobile=mobile)
        else:
            # 如果用户存在，检查用户密码
            if not user.check_password(pwd):
                return render(request, 'oauth_callback.html', {'account_errmsg': '用户名或密码错误'})

        # 将用户绑定openid
        try:
            OAuthQQUser.objects.create(openid=openid, user=user)
        except DatabaseError:
            return render(request, 'oauth_callback.html', {'qq_login_errmsg': 'QQ登录失败'})

        # 实现状态保持
        login(request, user)

        # 响应绑定结果
        next = request.GET.get('state')
        response = redirect(next)

        # 登录时用户名写入到cookie，有效期15天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
        merge_cart_cookie_to_redis(request=request, response=response)
        return response


class SinaAuthLoginView(View):
    def get(self, request):
        login_url = sinaweibopy3.client.get_authorize_url()
        return JsonResponse({"code": 0, "errmsg": 'OK', "login_url": login_url})


class SinaCallbackView(View):
    def get(self, request):
        code = request.GET.get('code')
        if not code:
            return HttpResponseForbidden('参数不足!')

        try:
            result = sinaweibopy3.client.request_access_token(code)
            # # 现在这个access_token还用不到
            # access_token = result.access_token
            uid = result.uid
            uid_secret = SecretOauth().dumps({'uid': uid})
        except Exception:
            return HttpResponseForbidden('OAuth2.0认证失败')

        try:
            sina_user_f = OAuthSinaUser.objects.get(uid=uid)
        except OAuthSinaUser.DoesNotExist:
            # 如果uid没绑定美多商城用户
            # 加密 uid-使用itsdangerous
            try:
                client_redis = get_redis_connection('uid_id')
            except Exception:
                return HttpResponseForbidden('连接失败...')
            client_redis.setex(uid_secret, constants.SECRET_UID_LIFE_TIME, uid)
            return render(request, 'sina_callback.html', context={'uid': uid_secret})
        else:
            # 如果uid已绑定美多商城用户
            # 实现状态保持
            sina_user = sina_user_f.user
            login(request, sina_user)
            # 重定向到主页
            response = redirect(reverse('contents:index'))
            # 登录时用户名写入到cookie，有效期15天,用户名暂时使用“微博用户+手机号”替代，实际应该为微博的昵称
            response.set_cookie('username',sina_user.username, max_age=3600 * 24 * 15)
            # merge_cart_cookie_to_redis(request=request, response=response)
            return response


class SinaUserView(View):
    def post(self, request):
        # 获得参数，正则校验参数
        message = request.body
        try:
            message = message.decode()
        except Exception:
            return JsonResponse({"status": 5005, "errmsg": '参数不全！'})
        if not re.match(r'^[:}{=\w",.-]+$', message):
            return JsonResponse({"status": 5005, "errmsg": '参数不全！'})
        try:
            mess_dict = json.loads(message)
        except Exception as e:
            # 已经是最后一道防线，如果已经突破到这里，则此段程序可能被完全攻破
            settings.LOGGER.error(e)  # 所以要记录一下
            return JsonResponse({"status": 5005, "errmsg": '参数不全！'})
        mobile = mess_dict.get('mobile')
        password = mess_dict.get('password')
        sms_code = mess_dict.get('sms_code')
        uid_secret = mess_dict.get('uid')
        if not all([mobile, password, sms_code, uid_secret]):
            return JsonResponse({"status": 5005, "errmsg": '参数不全！'})
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({"status": 5004, "errmsg": '请输入正确的手机号码'})
        # 判断密码是否合格
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return JsonResponse({"status": 5004, "errmsg": '请输入8-20位的密码'})

        # 判断短信验证码是否一致
        if not re.match(r'^[0-9]{6}$', sms_code):
            return JsonResponse({"status": 5001, "errmsg": '无效的短信验证码'})
        redis_conn = get_redis_connection('sms_code')
        sms_code_server = redis_conn.get('sms_%s' % mobile)
        if sms_code_server is None:
            return JsonResponse({"status": 5001, "errmsg": '无效的短信验证码'})
        if sms_code != sms_code_server.decode():
            return JsonResponse({"status": 5002, "errmsg": '短信验证码有误'})

        # 校验UID是否正确
        try:
            uid_dict = SecretOauth().loads(uid_secret)
            uid_receive = uid_dict['uid']
        except Exception:
            return JsonResponse({"status": 5003, "errmsg": 'uid有误'})
        try:
            client_redis = get_redis_connection('uid_id')
        except Exception:
            return JsonResponse({"status": 5006, "errmsg": '连接失败'})
        uid = client_redis.get(uid_secret).decode()
        if not uid:
            return JsonResponse({"status": 5003, "errmsg": 'uid有误'})
        if not uid == uid_receive:
            return JsonResponse({"status": 5003, "errmsg": 'uid有误'})

        try:
            OAuthSinaUser.objects.get(uid=uid)
            return JsonResponse({"status": 5003, "errmsg": 'uid有误,用户已经定义'})
        except Exception:
            pass

        # 如果没有绑定，则进行绑定
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            user = User.objects.create_user(username=(mobile + "微博user"), password=password, mobile=mobile)
        else:
            if not user.check_password(password):
                return JsonResponse({"status": 5004, "errmsg": '用户名已存在，密码错误'})
        try:
            OAuthSinaUser.objects.create(uid=uid, user=user)
        except DatabaseError:
            return JsonResponse({"status": 5004, "errmsg": '登陆失败'})

        try:
            client_redis.delete(uid_secret)
        except Exception:
            pass

        return JsonResponse({"status": 5000, "errmsg": '绑定成功'})
